import java.awt.* ;
import java.awt.event.* ;
import java.awt.geom.* ;
import javax.swing.* ;
import javax.swing.event.* ;

class ContentPanel extends JPanel {
	  Image bgimage = null;

	  ContentPanel() {
	    MediaTracker mt = new MediaTracker(this);
	    bgimage = Toolkit.getDefaultToolkit().getImage("images/menuMine.png");
	    mt.addImage(bgimage, 0);
	    try {
	      mt.waitForAll();
	    } catch (InterruptedException e) {
	      e.printStackTrace();
	    }
	  }

	  protected void paintComponent(Graphics g) {
	    super.paintComponent(g);
	    Graphics2D g2d = (Graphics2D) g;
	    int x = (this.getWidth() - bgimage.getWidth(null)) / 2;
	    int y = (this.getHeight() - bgimage.getHeight(null)) / 2;
	    g2d.drawImage(bgimage, x, y, null);
	  }
}

public class MineSweeper extends JFrame implements ActionListener ,
		MouseListener {
	int rows = 16 ;
	int cols = 16 ;
	int numMines = 40 ;
	GridLayout layout = new GridLayout ( rows , cols ) ;
	boolean [ ] mines = new boolean [ rows * cols ] ;
	boolean [ ] clickable = new boolean [ rows * cols ] ;
	boolean lost = false ;
	boolean won = false ;
	boolean firstMove ;
	boolean localPlayEnabled = false ;
	int [ ] numbers = new int [ rows * cols ] ;
	JButton [ ] buttons = new JButton [ rows * cols ] ;
	boolean [ ] clickdone = new boolean [ rows * cols ] ;
	JMenuItem newGameButton = new JMenuItem ( "new game" ) ;
	JLabel mineLabel = new JLabel ( "mines: " + numMines + " marked: 0" ) ;
	JPanel p = new JPanel ( ) ;
	
	//static Player player = null;
	
	/* Flag Image Resize */
	ImageIcon flag = new ImageIcon("images/flag.png") ;
	Image flagImg = flag.getImage() ;
	Image newFlagImg = flagImg.getScaledInstance( 20, 20,  java.awt.Image.SCALE_SMOOTH ) ;
	ImageIcon newFlag = new ImageIcon( newFlagImg );
	
	/* Mine Image Resize */
	ImageIcon mine = new ImageIcon("images/mine.png") ;
	Image mineImg = mine.getImage() ;
	Image newMineImg = mineImg.getScaledInstance( 20, 20,  java.awt.Image.SCALE_SMOOTH ) ;
	ImageIcon newMine = new ImageIcon( newMineImg );
	
	/* Game Over image */
	ImageIcon gameOverMine = new ImageIcon("images/gameOver.png") ;
	Image gameOverMineImg = gameOverMine.getImage() ;
	Image newGameOverMineImg = gameOverMineImg.getScaledInstance( 25, 25,  java.awt.Image.SCALE_SMOOTH ) ;
	ImageIcon newGameOverMine = new ImageIcon( newGameOverMineImg );
	
	/* Menu Items */
	JButton localPlay = new JButton ( "Local Play" ) ;
	JFrame menu = new JFrame ( "MAIN MENU" ) ;
	JLabel gameTitle = new JLabel ( "Competitive Minesweeper", newMine, 0 ) ;
	JButton networkPlay = new JButton ( "Network Play" ) ;
	JButton quit = new JButton ( "Quit" ) ;
	
	public void displayMenu ( ) {
		gameTitle.setFont( new Font ( "Serif", Font.BOLD, 20 ) ) ;
		localPlay.setSize ( 200, 200 ) ;
		localPlay.addActionListener ( this ) ;
		networkPlay.setSize ( 200, 200 ) ;
		networkPlay.addActionListener ( this ) ;
		quit.setSize ( 200, 200 ) ;
		quit.addActionListener( this ) ;
		menu.setContentPane( new ContentPanel ( ) ) ;
		Container contentPane = menu.getContentPane();	
		contentPane.setLayout ( new BoxLayout ( contentPane, BoxLayout.Y_AXIS) ) ;
		localPlay.setAlignmentX ( CENTER_ALIGNMENT );
		networkPlay.setAlignmentX ( CENTER_ALIGNMENT );
		quit.setAlignmentX ( CENTER_ALIGNMENT ) ;
		contentPane.add ( gameTitle ) ;
		contentPane.add ( localPlay );
		contentPane.add ( networkPlay ) ;
		contentPane.add ( quit ) ;
		menu.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//menu.pack ( ) ;
		menu.setSize( 400, 350 ) ;
		menu.setResizable ( false ) ;
		menu.setLocationRelativeTo ( null ) ;
		menu.addWindowListener( null ) ;
		menu.setVisible ( true ) ;
	}

	public MineSweeper ( ) {
		if ( ! localPlayEnabled ) {
			displayMenu ( ) ;
			localPlayEnabled = false ;
		}
		else {
			gameLayout ( ) ;
		}
	}

	public void gameLayout ( ) {
		p.setLayout ( layout ) ;
		gameSetup ( ) ;
		for ( int i = 0 ; i < ( rows * cols ) ; i ++ ) {
			p.add ( buttons [ i ] ) ;
		}
		this.add ( p ) ;
		this.add ( mineLabel , BorderLayout.SOUTH ) ;
		this.pack ( ) ;
		this.setVisible ( true ) ;
	}
	
	public void fillMines ( ) {
		int needed = numMines ;
		while ( needed > 0 ) {
			int x = ( int ) Math.floor ( Math.random ( ) * rows ) ;
			int y = ( int ) Math.floor ( Math.random ( ) * cols ) ;
			if ( ! mines [ ( rows * y ) + x ] ) {
				mines [ ( rows * y ) + x ] = true ;
				needed -- ;
			}
      else
        mines [ ( rows * y ) + x ] = false ;
		}
	}

	public void fillNumbers ( ) {
		for ( int x = 0 ; x < rows ; x ++ ) {
			for ( int y = 0 ; y < cols ; y ++ ) {
				int cur = ( rows * y ) + x ;
				if ( mines [ cur ] ) {
					numbers [ cur ] = 0 ;
					continue ;
				}
				int temp = 0 ;
				boolean l = ( x - 1 ) >= 0 ;
				boolean r = ( x + 1 ) < rows ;
				boolean u = ( y - 1 ) >= 0 ;
				boolean d = ( y + 1 ) < cols ;
				int left = ( rows * ( y ) ) + ( x - 1 ) ;
				int right = ( rows * ( y ) ) + ( x + 1 ) ;
				int up = ( rows * ( y - 1 ) ) + ( x ) ;
				int upleft = ( rows * ( y - 1 ) ) + ( x - 1 ) ;
				int upright = ( rows * ( y - 1 ) ) + ( x + 1 ) ;
				int down = ( rows * ( y + 1 ) ) + ( x ) ;
				int downleft = ( rows * ( y + 1 ) ) + ( x - 1 ) ;
				int downright = ( rows * ( y + 1 ) ) + ( x + 1 ) ;
				if ( u ) {
					if ( mines [ up ] ) {
						temp ++ ;
					}
					if ( l ) {
						if ( mines [ upleft ] ) {
							temp ++ ;
						}
					}
					if ( r ) {
						if ( mines [ upright ] ) {
							temp ++ ;
						}
					}
				}
				if ( d ) {
					if ( mines [ down ] ) {
						temp ++ ;
					}
					if ( l ) {
						if ( mines [ downleft ] ) {
							temp ++ ;
						}
					}
					if ( r ) {
						if ( mines [ downright ] ) {
							temp ++ ;
						}
					}
				}
				if ( l ) {
					if ( mines [ left ] ) {
						temp ++ ;
					}
				}
				if ( r ) {
					if ( mines [ right ] ) {
						temp ++ ;
					}
				}
				numbers [ cur ] = temp ;
			}
		}
	}

	public void gameSetup ( ) {
		firstMove = true ;
		for ( int x = 0 ; x < rows ; x ++ ) {
			for ( int y = 0 ; y < cols ; y ++ ) {
				mines [ ( rows * y ) + x ] = false ;
				clickdone [ ( rows * y ) + x ] = false ;
				clickable [ ( rows * y ) + x ] = true ;
				buttons [ ( rows * y ) + x ] = new JButton ( /*"" + ( x * y )*/) ;
				buttons [ ( rows * y ) + x ].setPreferredSize ( new Dimension (
						25 , 25 ) ) ;
				buttons [ ( rows * y ) + x ].addActionListener ( this ) ;
				buttons [ ( rows * y ) + x ].addMouseListener ( this ) ;
			}
		}
	}

	public void initializeBoard ( ) {
		if ( ! firstMove ) {
			fillMines ( ) ;
			fillNumbers ( ) ;
		}
	}
	public void resetBoard ( ) {
		firstMove = true ;
		for ( int x = 0 ; x < rows ; x ++ ) {
			for ( int y = 0 ; y < cols ; y ++ ) {
				mines [ ( rows * y ) + x ] = false ;
				clickdone [ ( rows * y ) + x ] = false ;
				clickable [ ( rows * y ) + x ] = true ;
				buttons [ ( rows * y ) + x ].setEnabled ( true ) ;
				buttons [ ( rows * y ) + x ].setIcon(null) ;
				buttons [ ( rows * y ) + x ].setText ( "" ) ;
			}
		}
		lost = false ;
		mineLabel.setText ( "mines: " + numMines + " marked: 0" ) ;
	}

	public static void main ( String [ ] args ) {
		//player = new Player("127.0.0.1", 9001);
		new MineSweeper ( ) ;
	}

	public void actionPerformed ( ActionEvent e ) {
		if ( e.getSource ( ) == localPlay ) {
			localPlayEnabled = true ;
			gameLayout ( ) ;
			menu.dispose ( ) ;
		}
		if ( e.getSource ( ) == networkPlay ) {
			JOptionPane.showMessageDialog ( null ,
					"Must implement network play" , "NETWORK PLAY" ,
					JOptionPane.ERROR_MESSAGE ) ;
		}
		if ( e.getSource ( ) == quit ) {
			menu.dispose ( ) ;
		}
		if ( ! won ) {
			for ( int x = 0 ; x < rows ; x ++ ) {
				for ( int y = 0 ; y < cols ; y ++ ) {
					if ( e.getSource ( ) == buttons [ ( rows * y ) + x ]
							&& ! won && clickable [ ( rows * y ) + x ] && firstMove ) {
						firstMove = false ;
						initializeBoard ( ) ;
						if ( mines [ ( rows * y ) + x ] ) {
							initializeBoard ( ) ;
						}
						doCheck ( x , y ) ;
						break ;
					}
					if ( e.getSource ( ) == buttons [ ( rows * y ) + x ]
							&& ! won && clickable [ ( rows * y ) + x ] && ! firstMove ) {
						doCheck ( x , y ) ;
						break ;
					}
				}
			}
		}
		if ( e.getSource ( ) == newGameButton ) {
			resetBoard ( ) ;
			won = false ;
			return ;

		}
		checkWin ( ) ;
	}

	public void mouseEntered ( MouseEvent e ) {

	}

	public void mouseExited ( MouseEvent e ) {

	}

	public void mousePressed ( MouseEvent e ) {
		if ( e.getButton ( ) == 3 ) {
			int n = 0 ;
			for ( int x = 0 ; x < rows ; x ++ ) {
				for ( int y = 0 ; y < cols ; y ++ ) {
					if ( e.getSource ( ) == buttons [ ( rows * y ) + x ] ) {
						clickable [ ( rows * y ) + x ] = ! clickable [ ( rows * y )
								+ x ] ;
					}
					if ( ! clickdone [ ( rows * y ) + x ] ) {
						if ( ! clickable [ ( rows * y ) + x ] ) {
							buttons [ ( rows * y ) + x ].setIcon ( newFlag ) ;
							n ++ ;
						} else {
							buttons [ ( rows * y ) + x ].setIcon ( null ) ;
							buttons [ ( rows * y ) + x ].setText ( "" ) ;
						}
						mineLabel.setText ( "mines: " + numMines + " marked: "
								+ n ) ;
					}
				}
			}
		}
	}

	public void mouseReleased ( MouseEvent e ) {

	}

	public void mouseClicked ( MouseEvent e ) {

	}

	public void doCheck ( int x , int y ) {
		int cur = ( rows * y ) + x ;
		boolean l = ( x - 1 ) >= 0 ;
		boolean r = ( x + 1 ) < rows ;
		boolean u = ( y - 1 ) >= 0 ;
		boolean d = ( y + 1 ) < cols ;
		int left = ( rows * ( y ) ) + ( x - 1 ) ;
		int right = ( rows * ( y ) ) + ( x + 1 ) ;
		int up = ( rows * ( y - 1 ) ) + ( x ) ;
		int upleft = ( rows * ( y - 1 ) ) + ( x - 1 ) ;
		int upright = ( rows * ( y - 1 ) ) + ( x + 1 ) ;
		int down = ( rows * ( y + 1 ) ) + ( x ) ;
		int downleft = ( rows * ( y + 1 ) ) + ( x - 1 ) ;
		int downright = ( rows * ( y + 1 ) ) + ( x + 1 ) ;

		clickdone [ cur ] = true ;
		buttons [ cur ].setEnabled ( false ) ;
		if ( numbers [ cur ] == 0 && ! mines [ cur ] && ! lost && ! won ) {
			if ( u && ! won ) {
				if ( ! clickdone [ up ] && ! mines [ up ] ) {
					clickdone [ up ] = true ;
					buttons [ up ].doClick ( ) ;
				}
				if ( l && ! won ) {
					if ( ! clickdone [ upleft ] && numbers [ upleft ] != 0
							&& ! mines [ upleft ] ) {
						clickdone [ upleft ] = true ;
						buttons [ upleft ].doClick ( ) ;
					}
				}
				if ( r && ! won ) {
					if ( ! clickdone [ upright ] && numbers [ upright ] != 0
							&& ! mines [ upright ] ) {
						clickdone [ upright ] = true ;
						buttons [ upright ].doClick ( ) ;
					}
				}
			}
			if ( d && ! won ) {
				if ( ! clickdone [ down ] && ! mines [ down ] ) {
					clickdone [ down ] = true ;
					buttons [ down ].doClick ( ) ;
				}
				if ( l && ! won ) {
					if ( ! clickdone [ downleft ] && numbers [ downleft ] != 0
							&& ! mines [ downleft ] ) {
						clickdone [ downleft ] = true ;
						buttons [ downleft ].doClick ( ) ;
					}
				}
				if ( r && ! won ) {
					if ( ! clickdone [ downright ]
							&& numbers [ downright ] != 0
							&& ! mines [ downright ] ) {
						clickdone [ downright ] = true ;
						buttons [ downright ].doClick ( ) ;
					}
				}
			}
			if ( l && ! won ) {
				if ( ! clickdone [ left ] && ! mines [ left ] ) {
					clickdone [ left ] = true ;
					buttons [ left ].doClick ( ) ;
				}
			}
			if ( r && ! won ) {
				if ( ! clickdone [ right ] && ! mines [ right ] ) {
					clickdone [ right ] = true ;
					buttons [ right ].doClick ( ) ;
				}
			}
		} else {
			buttons [ cur ].setText ( "" + numbers [ cur ] ) ;
			if ( ! mines [ cur ] && numbers [ cur ] == 0 ) {
				buttons [ cur ].setText ( "" ) ;
			}
		}
		if ( mines [ cur ] && ! won ) {
			clickdone [ cur ] = false ;
			clickable [ cur ] = true ;
			buttons [ cur ].setEnabled ( true ) ;
			buttons [ cur ].setIcon ( null ) ;
			buttons [ cur ].setText ( "" ) ;
			buttons [ cur ].setIcon ( newGameOverMine ) ;
			clickdone [ cur ] = true ;
			doLose ( ) ;
		}
	}

	public void checkWin ( ) {
		for ( int x = 0 ; x < rows ; x ++ ) {
			for ( int y = 0 ; y < cols ; y ++ ) {
				int cur = ( rows * y ) + x ;
				if ( ! clickdone [ cur ] ) {
					if ( mines [ cur ] ) {
						continue ;
					} else {
						return ;
					}
				}
			}
		}

		doWin ( ) ;
	}

	public void doWin ( ) {
		if ( ! lost && ! won ) {
			won = true ;
			JOptionPane.showMessageDialog ( null ,
					"You won!\nPress OK to return to the Main Menu" , "WINNER" ,
					JOptionPane.INFORMATION_MESSAGE ) ;
			this.dispose ( ) ;
			new MineSweeper ( ) ;
		}
	}

	public void doLose ( ) {
		if ( ! lost && ! won ) {
			lost = true ;
			for ( int i = 0 ; i < rows * cols ; i ++ ) {
				if ( ! clickdone [ i ] && mines [ i ] ) {
					buttons [ i ].setIcon( newMine ) ;
				}
				if ( ! clickdone [ i ] && ! mines [ i ] ) {
					buttons [ i ].doClick ( 0 ) ;
				}
			}
			JOptionPane.showMessageDialog ( null ,
					"Game Over!\nPress OK to return to the Main Menu" , "GAME OVER" ,
					JOptionPane.ERROR_MESSAGE ) ;
			this.dispose ( ) ;
			new MineSweeper ( ) ;
		}
	}
}